#include "rtspServer.h"
#include "mongoose.h"
#include "kernel_list.h"
#include "h264tortp.h"

typedef struct RtspSessionNode
{
    struct list_head glist;
    int count;
    pthread_mutex_t lock;
    RtpSession session;
} RtspSessionNode;
RtspSessionNode *RtspSessionList = NULL;

static const char *rtsp_parse_headers(const char *s, const char *end,
                                      int len, struct http_message *req)
{
    int i = 0;
    while (i < (int)ARRAY_SIZE(req->header_names) - 1)
    {
        struct mg_str *k = &req->header_names[i], *v = &req->header_values[i];

        s = mg_skip(s, end, ": ", k);
        s = mg_skip(s, end, "\r\n", v);

        while (v->len > 0 && v->p[v->len - 1] == ' ')
        {
            v->len--; /* Trim trailing spaces in header value */
        }

        /*
     * If header value is empty - skip it and go to next (if any).
     * NOTE: Do not add it to headers_values because such addition changes API
     * behaviour
     */
        if (k->len != 0 && v->len == 0)
        {
            continue;
        }

        if (k->len == 0 || v->len == 0)
        {
            k->p = v->p = NULL;
            k->len = v->len = 0;
            break;
        }

        if (!mg_ncasecmp(k->p, "Content-Length", 14))
        {
            req->body.len = (size_t)to64(v->p);
            req->message.len = len + req->body.len;
        }

        i++;
    }

    return s;
}
RtspSessionNode * insertSession(int sessionId)
{
    RtspSessionNode *e = NULL;
    RtspSessionNode *tmp = NULL;
    RtspSessionNode *node = NULL;
    pthread_mutex_lock(&RtspSessionList->lock);

    list_for_each_entry_safe(e, tmp, &RtspSessionList->glist, glist)
    {
        if(e->session.sessionId==sessionId) //事件
        {
            node = e;
            break;
        }
    }

    if (&e->glist == &(RtspSessionList->glist) ||sessionId==0)
    {
        node = (RtspSessionNode *)malloc(sizeof(RtspSessionNode));
        memset(node, 0, sizeof(RtspSessionNode));
        node->session.sessionId=(int)node;
        node->session.ssrcNum=RtspSessionList->count+1;
        list_add(&node->glist, &RtspSessionList->glist);
        RtspSessionList->count++;
    }
    pthread_mutex_unlock(&RtspSessionList->lock);
    return node;
}
int getCSeq(char *p)
{
    struct http_message header;
    int len = strlen(p);
    char tmp[50] = {0};
    struct mg_str *str;
    int cs = 0;
    rtsp_parse_headers(p, p + len, len, &header);
    str = mg_get_http_header(&header, "CSeq");
    if (str && str->len > 0)
    {
        memcpy(tmp, str->p, str->len);
        cs = atoi(tmp);
    }
    return cs;
}
int getSessionId(char *p)
{
    struct http_message header;
    int len = strlen(p);
    char tmp[50] = {0};
    struct mg_str *str;
    int cs = 0;
    rtsp_parse_headers(p, p + len, len, &header);
    str = mg_get_http_header(&header, "Session");
    if (str && str->len > 0)
    {
        memcpy(tmp, str->p, str->len);
        cs = atoi(tmp);
    }
    return cs;
}
void getSetupPort(char *content,int *rtpPort,int*rtcpPort)
{
    char buf[500]={0};
    int count=sprintf(buf,"%s",content);
    buf[count-2]=0;//去掉\r\n
    char *p=strstr(buf,"client_port");
    if(!p)
    {
        return;
    }
    p+=12;
    char *argv[2];
    int i=0;
    int in=0;
    char *inner_ptr=NULL;
    while((argv[in]=strtok_r(p, "-", &inner_ptr))!=NULL)//多线程下必须这么用
    {
        in++;
        p=NULL;
        if(in>10)
            break;
    }
    if(in==2)
    {
        *rtpPort=atoi(argv[0]);
        *rtcpPort=atoi(argv[1]);
    }
    
}
void getMediaUrl(char *content,char *dst)
{
    char buf[500]={0};
    int count=sprintf(buf,"%s",content);
    buf[count-2]=0;//去掉\r\n
    char *p=strstr(buf,"rtsp://");
    if(!p)
    {
        return;
    }
    p+=7;
    char *p2=strstr(p,"RTSP/1.0");
    if(!p2)
        return;
    *(p2-1)=0;
    char *argv[10];
    int i=0;
    int in=0;
    char *inner_ptr=NULL;
    while((argv[in]=strtok_r(p, "/", &inner_ptr))!=NULL)//多线程下必须这么用
    {
        in++;
        p=NULL;
        if(in>10)
            break;
    }
    if(in>0)
        sprintf(dst,argv[in-1]);
}

void ResponseOptions(char *buffer, char *response)
{
    int CSeq = getCSeq(buffer);
    int count = 0;
    count += sprintf(response, "RTSP/1.0 200 OK\r\n");
    count += sprintf(&response[count], "CSeq: %d\r\n", CSeq);
    count += sprintf(&response[count], "{\"Public: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE\"}\r\n");
    count += sprintf(&response[count], "\r\n\r\n");
}
void ResponseDescribe(char *buffer, char *response)
{

    int CSeq = getCSeq(buffer);
    int count = 0;
    char sdp[500]={0};
    char name[100]={0};
    int sdpLen=0;
    getMediaUrl(buffer,name);
    sprintf(name,"%s.h264",name);
    if(access(name,F_OK)!=0)
    {
        return;
    }
    count += sprintf(response, "RTSP/1.0 200 OK\r\n");
    count += sprintf(&response[count], "CSeq: %d\r\n",CSeq);
    count += sprintf(&response[count], "Content-Type: application/sdp\r\n");
    sdpLen += sprintf(&sdp[sdpLen], "v=0\r\n");//sdp版本
    sdpLen += sprintf(&sdp[sdpLen], "m=video %d RTP/AVP 96\r\n",RTP_PORT);
    sdpLen += sprintf(&sdp[sdpLen], "a=rtpmap:96 H264/9000\r\n");
    sdpLen += sprintf(&sdp[sdpLen], "a=framerate:25\r\n");
    sdpLen += sprintf(&sdp[sdpLen], "c=IN IP4 0.0.0.0\r\n");
    count += sprintf(&response[count], "Content-Length: %d",sdpLen);
    count += sprintf(&response[count], "\r\n\r\n");
    count += sprintf(&response[count], "%s",sdp);
}
void ResponseSetup(char *ipAddr,char *buffer, char *response)
{
    int CSeq = getCSeq(buffer);
    int count = 0;
    RtspSessionNode *e = NULL;
    e = insertSession(0);
    char name[100]={0};
    getSetupPort(buffer,&e->session.rtpPort,&e->session.rtcpPort);
    getMediaUrl(buffer,name);
    sprintf(e->session.mediaName,"%s.h264",name);
    count += sprintf(response, "RTSP/1.0 200 OK\r\n");
    count += sprintf(&response[count], "CSeq: %d\r\n", CSeq);
    count += sprintf(&response[count], "Session:       %d;timeout=60\r\n",e->session.sessionId);
    count += sprintf(&response[count], "Transport: RTP/AVP;unicast;client_port=%d-%d;",e->session.rtpPort,e->session.rtcpPort);
    count += sprintf(&response[count], "server_port=%d-%d;ssrc=%d",RTP_PORT,RTP_PORT+1,e->session.ssrcNum);
    count += sprintf(&response[count], "\r\n\r\n");
    initRtpServer(&e->session);
    addMissionToRtp(&e->session,ipAddr,e->session.rtpPort);
}
void ResponsePlay(char *buffer, char *response)
{
    int CSeq = getCSeq(buffer);
    int sessionId=getSessionId(buffer);

    int count = 0;
    count += sprintf(response, "RTSP/1.0 200 OK\r\n");
    count += sprintf(&response[count], "CSeq: %d\r\n", CSeq);
    count += sprintf(&response[count], "Session:       %d",sessionId);
    count += sprintf(&response[count], "\r\n\r\n");
    RtspSessionNode *e = NULL;
    e = insertSession(sessionId);
    startRtpServer(&e->session);

}
void ResponseTearDown(char *buffer, char *response)
{
    int CSeq = getCSeq(buffer);
    int sessionId=getSessionId(buffer);
    int count = 0;
    RtspSessionNode *e = NULL;
    e = insertSession(sessionId);
    count += sprintf(response, "RTSP/1.0 200 OK\r\n");
    count += sprintf(&response[count], "CSeq: %d\r\n", CSeq);
    count += sprintf(&response[count], "\r\n\r\n");
    e->session.blStop=1;
}
void ResponsePause(char *buffer, char *response)
{
    int CSeq = getCSeq(buffer);
    int count = 0;
    RtspSessionNode *e = NULL;
    int sessionId=getSessionId(buffer);
    e = insertSession(sessionId);
    count += sprintf(response, "RTSP/1.0 200 OK\r\n");
    count += sprintf(&response[count], "CSeq: %d\r\n", CSeq);
    count += sprintf(&response[count], "\r\n\r\n");
    e->session.blPause=1;
}
static void ev_handler(struct mg_connection *nc, int ev, void *p)
{
    struct mbuf *msg = &nc->recv_mbuf;
    char ipAddr[50] = {0};

    char tmp[5000] = {0};
    char content[5000]={0};
    int ret;
    (void)p;
    int i = 0;
    int len = 0;
    switch (ev)
    {
    case MG_EV_RECV:
        sprintf(ipAddr, "%s", inet_ntoa(nc->sa.sin.sin_addr));
        if(msg->len<5000)
        {
            memcpy(content,msg->buf,msg->len);
        }
        else
            break;

        if (strncmp(msg->buf, "OPTIONS", 7) == 0)
        {
            ResponseOptions(content, tmp);
            mg_send(nc, tmp, strlen(tmp));
        }
        else if (strncmp(msg->buf, "DESCRIBE", 8) == 0)
        {
            ResponseDescribe(content, tmp);
            mg_send(nc, tmp, strlen(tmp));
        }
        else if (strncmp(msg->buf, "SETUP", 5) == 0)
        {
            ResponseSetup(ipAddr,content, tmp);
            mg_send(nc, tmp, strlen(tmp));
        }
        else if (strncmp(msg->buf, "TEARDOWN", 8) == 0)
        {
            ResponseTearDown(content, tmp);
            mg_send(nc, tmp, strlen(tmp));
        }
        else if (strncmp(msg->buf, "PLAY", 4) == 0)
        {
            ResponsePlay(content, tmp);
            mg_send(nc, tmp, strlen(tmp));
        }
        else if (strncmp(msg->buf, "PAUSE", 5) == 0)
        {
            ResponsePause(content, tmp);
            mg_send(nc, tmp, strlen(tmp));
        }

        mbuf_remove(msg, msg->len);
        break;
    }
}

int startServer()
{
    struct mg_mgr mgr;
    struct mg_connection *nc;
    mg_mgr_init(&mgr, NULL);
    char port[50] = {0};
    sprintf(port, "%s", "554");
    RtspSessionList = (RtspSessionNode *)malloc(sizeof(RtspSessionNode));
    RtspSessionList->count = 0;
    INIT_LIST_HEAD(&RtspSessionList->glist);
    pthread_mutex_init(&RtspSessionList->lock, NULL);
    nc = mg_bind(&mgr, port, ev_handler);

    if (nc == NULL)
    {
        AppLogOut("%s    bind faild!", __FUNCTION__);
        return 0;
    }
    while (1)
    {
        mg_mgr_poll(&mgr, 1000);
    }
    mg_mgr_free(&mgr);
    return 0;
}
